#include "stdafx.h"
#include "UploadHttpPool.h"
#include "Data/CharsetConvert.h"
#include "CriticalSection.h"
#include "WorkItem.h"

const int nSendBufLen =SLICESIZE;
const CString strBoundary = _T("----------------------------f45118d49146");

CUploadHttpPool::CUploadHttpPool()
{

}

CUploadHttpPool::~CUploadHttpPool()
{

}

const CString strUploadTemplate = _T("POST %s HTTP/1.1\r\nHost:%s:%d\r\nAccept: */*\r\nContent-Type: multipart/form-data; boundary=%s\r\nContent-Length:%I64d\r\n\r\n");

bool CUploadHttpPool::AddUploadFile(IUploadRecv* pUR,const CString& strUrl,const CString& strAppCode,const CString& strUserId,const CString& strToken,const CString& strFileId,const CString& strFilePath)
{
	
	return AddUploadFile(pUR,strUrl,strAppCode,strUserId,strToken,strFileId,strFilePath,0,0,0,0);
}

bool CUploadHttpPool::AddUploadFile(IUploadRecv* pUR,const CString& strUrl,const CString& strAppCode,const CString& strUserId,const CString& strToken,const CString& strFileId,const CString& strFilePath,LONGLONG nFileSize,LONGLONG nCurNum,LONGLONG nTotalNum,LONGLONG nSliceSize)
{
	if (nFileSize == 0)
	{
		try
		{
			CFile file(strFilePath,CFile::modeRead|CFile::shareDenyWrite);
			nFileSize = file.GetLength();
			file.Close();
		}
		catch (CException* e)
		{
			TCHAR err[255]={0};
			e->GetErrorMessage(err,255);
			OutputDebugString(err);
			return false;
		}
	}
	
	

	CString strMimeHead;
	strMimeHead += MakeFormItem(strFilePath);


	
	


	CString strMimeTail = _T("\r\n");
	if (nCurNum!=0 && nTotalNum!=0 && nSliceSize!=0)
	{
		
		CString strSliceTmp =  _T("{'num':%I64d,'total':%I64d,'slicesize':%I64d}");
		CString strSliceJson ;
		strSliceJson.Format(strSliceTmp,nCurNum,nTotalNum,nSliceSize);
		strMimeTail += MakeFormItem(_T("slice"),strSliceJson);
	}

	strMimeTail += MakeFormItem(_T("appcode"),strAppCode);
	strMimeTail += MakeFormItem(_T("userid"),strUserId);
	strMimeTail += MakeFormItem(_T("token"),strToken);
	strMimeTail += MakeFormItem(_T("fileid"),strFileId);
	strMimeTail +=_T("--")+strBoundary+_T("--\r\n");


	DWORD dwServiceType;
	CString strServer;
	CString strObject;
	INTERNET_PORT nPort;
	if(AfxParseURL(strUrl,dwServiceType,strServer,strObject,nPort))
	{
		/*LONGLONG nThisSize =nSliceSize;
		if (nTotalNum == nCurNum)
		{
			nThisSize = nFileSize - (nTotalNum-1)*nSliceSize;
		}*/

		CString strTemp;
		strTemp.Format(strUploadTemplate,strObject,strServer,nPort,strBoundary,(LONGLONG)nSliceSize+strMimeHead.GetLength()+strMimeTail.GetLength());

		strTemp += strMimeHead;
		string strBuf = ct2cu(strTemp);

		return AddUploadTask(pUR,strBuf.c_str(),strBuf.size(),strServer,nPort,strFilePath,nFileSize,strMimeTail,nCurNum,nTotalNum,nSliceSize);

	}

	return false;
}

CString CUploadHttpPool::MakeFormItem(const CString& strKey,const CString& strValue)
{
	CString strTemp = _T("--%s\r\nContent-Disposition: form-data; name=\"%s\"\r\n\r\n%s\r\n");
	CString strRet;
	strRet.Format(strTemp,strBoundary,strKey,strValue);
	return strRet;
}

CString CUploadHttpPool::MakeFormItem(const CString& strFilePath)
{

	CString strExt;
	try
	{
		CFile file(strFilePath,CFile::modeRead|CFile::shareDenyWrite);
		strExt = file.GetFileName();
		int nPos = strExt.ReverseFind(_T('.'));
		if (nPos > 0)
		{
			strExt = strExt.Right(strExt.GetLength() - nPos);
		}
	}
	
	catch (CException* e)
	{
		TCHAR err[255]={0};
		e->GetErrorMessage(err,255);
		OutputDebugString(err);
	}

	CString strTemp = _T("--%s\r\nContent-Disposition: form-data; name=\"sourcefile\"; filename=\"newfile%s\"\r\nContent-Type: application/octet-stream\r\n\r\n");
	CString strRet;
	strRet.Format(strTemp,strBoundary,strExt);
	return strRet;
}

bool CUploadHttpPool::AddUploadTask(IUploadRecv* pUR,const char * pBuf,int nLen,const CString& strHost,int nPort,const CString& strFilePath,LONGLONG nFileSize,const CString& strMimeTail,LONGLONG nCurNum,LONGLONG nTotalNum,LONGLONG nSliceSize)
{
	CritScope scope(m_pTaskSection);

	UploadHttpTaskData *pData = new UploadHttpTaskData;
	pData->pRecv = NULL;
	pData->pBuf = new char[nLen];
	memcpy(pData->pBuf,pBuf,nLen);

	pData->nLen = nLen;
	pData->strHost = strHost;
	pData->nPort = nPort;
	pData->nHasSended = 0;

	pData->pUR = pUR;
	pData->strFilePath = strFilePath;
	pData->nFileSize = nFileSize;
	pData->nFileHasSended = 0;
	pData->strMimeTail = strMimeTail;
	pData->bSendFinish = false;

	pData->nCurNum = nCurNum;
	pData->nTotalNum = nTotalNum;
	pData->nSliceSize = nSliceSize;

	m_qTaskDatas.push(pData);
	ActiveProcess();

	return false;

}

void CUploadHttpPool::Process()
{
	if (GetHttpStatus() == HttpStatus_Run)
	{
		CWorkItem * pItem = GetCurFreeItem();

		while(pItem)
		{	
			UploadHttpTaskData * pData =(UploadHttpTaskData *) GetFrontTask();
			if (pData)
			{
				if (pItem->DoConnect(pData->strHost,pData->nPort))
				{
					SetItem(pItem,pData);		
				}
				else
				{
					if (pData->pUR)
					{
						pData->pUR->OnUploadError(ErrorType_Connect);
					}

					if (pData->pBuf)
					{
						delete pData->pBuf;
						pData->pBuf = NULL;
					}

					delete pData;

					OnClose(pItem);
				}
				PopTask();
			}
			else
			{
				break;
			}

			pItem = GetCurFreeItem();


		}
	}

}

void CUploadHttpPool::DoSetContentCoding(CWorkItem*,const string&)
{

}

void CUploadHttpPool::DoCallBack(CWorkItem*pItem,char* pBuf,int nLen)
{
	//UploadHttpTaskData * pData = (UploadHttpTaskData*)GetItemData(pItem);//it->second;
	//if (pData && pData->pUR)
	//{
	//	pData->pUR->OnReturn(pBuf,nLen);
	//}

	CritScope scope(m_pMapSection);
	map<CWorkItem*,HttpTaskData*>::iterator it= m_mapRecvs.find(pItem);
	if (it != m_mapRecvs.end())
	{
		HttpTaskData * pRecv = it->second;
		if (pRecv)
		{
			UploadHttpTaskData * pData = (UploadHttpTaskData *)pRecv;
			if (pData->pUR)
			{
				pData->pUR->OnReturn(pBuf,nLen);
			}
		}

	}
}

void CUploadHttpPool::OnSockClose(CWorkItem*pItem)
{
	/*UploadHttpTaskData * pData = (UploadHttpTaskData *)GetItemData(p);
	if (pData&&pData->pUR)
	{
		pData->pUR->OnUploadError(ErrorType_Finish);
	}*/

	CritScope scope(m_pMapSection);
	map<CWorkItem*,HttpTaskData*>::iterator it= m_mapRecvs.find(pItem);
	if (it != m_mapRecvs.end())
	{
		HttpTaskData * pRecv = it->second;
		if (pRecv)
		{
			UploadHttpTaskData * pData = (UploadHttpTaskData *)pRecv;
			if (pData->pUR)
			{
				pData->pUR->OnUploadError(ErrorType_Finish);
			}
		}

	}

	CHttpPool::OnSockClose(pItem);
}

void CUploadHttpPool::OnClose(CWorkItem*p)
{
	 CHttpPool::OnClose(p);
}

void CUploadHttpPool::OnSendData(CWorkItem* p)
{
	CHttpPool::OnSendData(p);
}

void CUploadHttpPool::DoSetContentLength(CWorkItem*,int)
{

}

bool CUploadHttpPool::GetMimeTailBuf(UploadHttpTaskData *pData)
{
	if (pData->bSendFinish)
	{
		return false;
	}

	pData->bSendFinish = true;
	string szMimeTail = ct2cu(pData->strMimeTail);
	memcpy(pData->pBuf,szMimeTail.c_str(),szMimeTail.size());
	pData->nLen = szMimeTail.size();

	return true;
}

int CUploadHttpPool::GetFileBuf(const CString& strFilePath,char* pBuf,int nLen,LONGLONG nOffSet)
{
	try
	{
		CFile file(strFilePath,CFile::modeRead|CFile::shareDenyWrite);
		file.Seek(nOffSet,CFile::begin);
		int nRet = file.Read(pBuf,nLen);
		if (nRet > 0)
		{
			return nRet;
		}

	}
	
	catch (CException* e)
	{
		TCHAR err[255]={0};
		e->GetErrorMessage(err,255);
		OutputDebugString(err);
	}
	return -1;
}

bool CUploadHttpPool::GetFileSendOneBuf(UploadHttpTaskData * pData)
{
	if (pData->nFileHasSended != 0)
	{
		return false;
	}

	LONGLONG nOffSet = 0;
	if (pData->nCurNum != pData->nTotalNum)
	{
		nOffSet =(pData->nCurNum - 1)*pData->nSliceSize;
	}
	else
	{
		nOffSet = pData->nFileSize - pData->nSliceSize;
	}

	int nRet = GetFileBuf(pData->strFilePath,pData->pBuf,pData->nLen,nOffSet);
	if (nRet>0)
	{
		pData ->nLen = nRet;
		pData->nFileHasSended = nRet;

		return true;
	}
	return false;
}

bool CUploadHttpPool::GetFileSendBuf(UploadHttpTaskData *pData)
{
	LONGLONG nLeft = pData->nFileSize -pData->nFileHasSended;
	if (nLeft ==0)
	{
		return false;
	}
	else if(nLeft <0)
	{
		ASSERT(FALSE);
		return false;	
	}


	LONGLONG nBufToRead;
	if (nLeft>=pData->nLen)
	{
		nBufToRead = pData->nLen;
	}
	else
	{
		nBufToRead = nLeft;
	}


	int nRet = GetFileBuf(pData->strFilePath,pData->pBuf,pData->nLen,pData->nFileHasSended);
	if (nRet > 0)
	{
		pData->nFileHasSended += nRet;
		pData->nLen = nRet;
		return true;
	}
	


	return false;

}

void CUploadHttpPool::SendData(CWorkItem* pItem)
{
	if (pItem)
	{	
		UploadHttpTaskData * pData =(UploadHttpTaskData *) GetItemData(pItem);
		if (pData)
		{
			
			int nRet = pItem->SendData(pData->pBuf+pData->nHasSended,pData->nLen - pData->nHasSended);
			if (nRet>0)
			{
				if (pData->bSendFinish)
				{
					return;
				}

				pData->nHasSended += nRet;
				if (pData->nHasSended < pData->nLen)
				{
					OnSendData(pItem);
				}
				else if(pData->nHasSended == pData->nLen)
				{
					if (pData->pUR)
					{
						pData->pUR->OnFileSend(pData);
					}

					if (pData->nLen < nSendBufLen)
					{
						delete pData->pBuf;
						pData->pBuf = new char[nSendBufLen];
						pData->nLen = nSendBufLen;
					}
					
					memset(pData->pBuf,0,pData->nLen);
					pData->nHasSended = 0;

					if (pData->nCurNum !=0 && pData->nTotalNum!=0 && pData->nSliceSize!=0)
					{
						if (GetFileSendOneBuf(pData))
						{
							OnSendData(pItem);
						}
						else
						{
							if (pData->nFileHasSended > 0)
							{
								if (GetMimeTailBuf(pData))
								{
									OnSendData(pItem);
								}
							}
							else
							{
								if (pData->pUR)
								{
									pData->pUR->OnUploadError(ErrorType_Upload);
								}
							}

						}
					
					}
					else
					{
						if (GetFileSendBuf(pData))
						{
							OnSendData(pItem);
						}
						else
						{
							if (pData->nFileHasSended == pData->nFileSize )
							{
								if (GetMimeTailBuf(pData))
								{
									OnSendData(pItem);
								}

							}
							else
							{
								if (pData->pUR)
								{
									pData->pUR->OnUploadError(ErrorType_Upload);
								}
							}

						}
						
					}

										
				}
				else
				{
					ASSERT(FALSE);
				}

			}
			else if(nRet < 0)
			{
				/*if (pData->pUR)
				{
					pData->pUR->OnUploadError(ErrorType_Send);
				}*/

				OnClose(pItem);
			}
			else
			{
				Sleep(100);
				//OnSendData(pItem);
			}



		}

	}
}

void CUploadHttpPool::OnUploadError(ErrorType)
{

}

void CUploadHttpPool::OnFileSend(UploadHttpTaskData *)
{

}